package table_test

import (
	"bytes"
	"testing"

	"github.com/stretchr/testify/assert"

	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
	"github.com/aquasecurity/trivy/pkg/report/table"
	"github.com/aquasecurity/trivy/pkg/types"
)

func Test_vulnerabilityRenderer_Render(t *testing.T) {
	tests := []struct {
		name               string
		result             types.Result
		want               string
		includeNonFailures bool
		showSuppressed     bool
	}{
		{
			name: "happy path full",
			result: types.Result{
				Target: "test",
				Class:  types.ClassLangPkg,
				Vulnerabilities: []types.DetectedVulnerability{
					{
						VulnerabilityID:  "CVE-2020-0001",
						PkgName:          "foo",
						InstalledVersion: "1.2.3",
						PrimaryURL:       "https://avd.aquasec.com/nvd/cve-2020-0001",
						Status:           dbTypes.StatusWillNotFix,
						Vulnerability: dbTypes.Vulnerability{
							Title:       "foobar",
							Description: "baz",
							Severity:    "HIGH",
						},
					},
				},
				// It won't be shown as `showSuppressed` is false.
				ModifiedFindings: []types.ModifiedFinding{
					{
						Type:   types.FindingTypeVulnerability,
						Status: types.FindingStatusIgnored,
						Source: ".trivyignore",
						Finding: types.DetectedVulnerability{
							VulnerabilityID: "CVE-2020-0002",
						},
					},
				},
			},
			want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)

┌─────────┬───────────────┬──────────┬──────────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │    Status    │ Installed Version │ Fixed Version │                   Title                   │
├─────────┼───────────────┼──────────┼──────────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo     │ CVE-2020-0001 │ HIGH     │ will_not_fix │ 1.2.3             │               │ foobar                                    │
│         │               │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2020-0001 │
└─────────┴───────────────┴──────────┴──────────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
		},
		{
			name: "happy path with filePath in result",
			result: types.Result{
				Target: "test",
				Class:  types.ClassLangPkg,
				Vulnerabilities: []types.DetectedVulnerability{
					{
						VulnerabilityID:  "CVE-2020-0001",
						PkgName:          "foo",
						PkgPath:          "foo/bar",
						InstalledVersion: "1.2.3",
						FixedVersion:     "3.4.5",
						PrimaryURL:       "https://avd.aquasec.com/nvd/cve-2020-0001",
						Status:           dbTypes.StatusFixed,
						Vulnerability: dbTypes.Vulnerability{
							Title:       "foobar",
							Description: "baz",
							Severity:    "HIGH",
						},
					},
				},
			},
			want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)

┌───────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│  Library  │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │                   Title                   │
├───────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo (bar) │ CVE-2020-0001 │ HIGH     │ fixed  │ 1.2.3             │ 3.4.5         │ foobar                                    │
│           │               │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2020-0001 │
└───────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
		},
		{
			name: "no title for vuln and missing primary link",
			result: types.Result{
				Target: "test",
				Class:  types.ClassLangPkg,
				Vulnerabilities: []types.DetectedVulnerability{
					{
						VulnerabilityID:  "CVE-2020-0001",
						PkgName:          "foo",
						InstalledVersion: "1.2.3",
						FixedVersion:     "3.4.5",
						Status:           dbTypes.StatusFixed,
						Vulnerability: dbTypes.Vulnerability{
							Description: "foobar",
							Severity:    "HIGH",
						},
					},
				},
			},
			want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)

┌─────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title  │
├─────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ foo     │ CVE-2020-0001 │ HIGH     │ fixed  │ 1.2.3             │ 3.4.5         │ foobar │
└─────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘
`,
		},
		{
			name: "long title for vuln",
			result: types.Result{
				Target: "test",
				Class:  types.ClassLangPkg,
				Vulnerabilities: []types.DetectedVulnerability{
					{
						VulnerabilityID:  "CVE-2020-1234",
						PkgName:          "foo",
						InstalledVersion: "1.2.3",
						FixedVersion:     "3.4.5",
						PrimaryURL:       "https://avd.aquasec.com/nvd/cve-2020-1234",
						Status:           dbTypes.StatusFixed,
						Vulnerability: dbTypes.Vulnerability{
							Title:       "a b c d e f g h i j k l m n o p q r s t u v",
							Description: "foobar",
							Severity:    "HIGH",
						},
					},
				},
			},
			want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)

┌─────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │                   Title                   │
├─────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo     │ CVE-2020-1234 │ HIGH     │ fixed  │ 1.2.3             │ 3.4.5         │ a b c d e f g h i j k l...                │
│         │               │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2020-1234 │
└─────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘
`,
		},
		{
			name: "happy path with vulnerability origin graph with direct dependency info",
			result: types.Result{
				Target: "package-lock.json",
				Class:  types.ClassLangPkg,
				Type:   "npm",
				Packages: []ftypes.Package{
					{
						ID:           "node-fetch@1.7.3",
						Name:         "node-fetch",
						Version:      "1.7.3",
						Relationship: ftypes.RelationshipIndirect,
					},
					{
						ID:           "isomorphic-fetch@2.2.1",
						Name:         "isomorphic-fetch",
						Version:      "2.2.1",
						Relationship: ftypes.RelationshipIndirect,
						DependsOn: []string{
							"node-fetch@1.7.3",
						},
					},
					{
						ID:           "fbjs@0.8.18",
						Name:         "fbjs",
						Version:      "0.8.18",
						Relationship: ftypes.RelationshipIndirect,
						DependsOn: []string{
							"isomorphic-fetch@2.2.1",
						},
					},
					{
						ID:           "sanitize-html@1.20.0",
						Name:         "sanitize-html",
						Version:      "1.20.0",
						Relationship: ftypes.RelationshipDirect,
					},
					{
						ID:           "styled-components@3.1.3",
						Name:         "styled-components",
						Version:      "3.1.3",
						Relationship: ftypes.RelationshipDirect,
						DependsOn: []string{
							"fbjs@0.8.18",
						},
					},
				},
				Vulnerabilities: []types.DetectedVulnerability{
					{
						VulnerabilityID: "CVE-2022-0235",
						PkgID:           "node-fetch@1.7.3",
						PkgName:         "node-fetch",
						Vulnerability: dbTypes.Vulnerability{
							Title:       "foobar",
							Description: "baz",
							Severity:    "HIGH",
						},
						InstalledVersion: "1.7.3",
						FixedVersion:     "2.6.7, 3.1.1",
						Status:           dbTypes.StatusFixed,
					},
					{
						VulnerabilityID: "CVE-2021-26539",
						PkgID:           "sanitize-html@1.20.0",
						PkgName:         "sanitize-html",
						Vulnerability: dbTypes.Vulnerability{
							Title:       "foobar",
							Description: "baz",
							Severity:    "MEDIUM",
						},
						InstalledVersion: "1.20.0",
						FixedVersion:     "2.3.1",
						Status:           dbTypes.StatusFixed,
					},
				},
			},
			want: `
package-lock.json (npm)
=======================
Total: 2 (MEDIUM: 1, HIGH: 1)

┌───────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│    Library    │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │ Title  │
├───────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ node-fetch    │ CVE-2022-0235  │ HIGH     │ fixed  │ 1.7.3             │ 2.6.7, 3.1.1  │ foobar │
├───────────────┼────────────────┼──────────┤        ├───────────────────┼───────────────┤        │
│ sanitize-html │ CVE-2021-26539 │ MEDIUM   │        │ 1.20.0            │ 2.3.1         │        │
└───────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘

Dependency Origin Tree (Reversed)
=================================
package-lock.json
├── node-fetch@1.7.3, (MEDIUM: 0, HIGH: 1)
│   └── ...(omitted)...
│       └── styled-components@3.1.3
└── sanitize-html@1.20.0, (MEDIUM: 1, HIGH: 0)
`,
		},
		{
			name: "happy path with vulnerability origin graph with unknown relationships",
			result: types.Result{
				Target: "package-lock.json",
				Class:  types.ClassLangPkg,
				Type:   "npm",
				Packages: []ftypes.Package{
					{
						ID:           "node-fetch@1.7.3",
						Name:         "node-fetch",
						Version:      "1.7.3",
						Relationship: ftypes.RelationshipUnknown,
					},
					{
						ID:           "isomorphic-fetch@2.2.1",
						Name:         "isomorphic-fetch",
						Version:      "2.2.1",
						Relationship: ftypes.RelationshipUnknown,
						DependsOn: []string{
							"node-fetch@1.7.3",
						},
					},
					{
						ID:           "fbjs@0.8.18",
						Name:         "fbjs",
						Version:      "0.8.18",
						Relationship: ftypes.RelationshipUnknown,
						DependsOn: []string{
							"isomorphic-fetch@2.2.1",
						},
					},
					{
						ID:           "styled-components@3.1.3",
						Name:         "styled-components",
						Version:      "3.1.3",
						Relationship: ftypes.RelationshipUnknown,
						DependsOn: []string{
							"fbjs@0.8.18",
						},
					},
				},
				Vulnerabilities: []types.DetectedVulnerability{
					{
						VulnerabilityID: "CVE-2022-0235",
						PkgID:           "node-fetch@1.7.3",
						PkgName:         "node-fetch",
						Vulnerability: dbTypes.Vulnerability{
							Title:       "foobar",
							Description: "baz",
							Severity:    "HIGH",
						},
						InstalledVersion: "1.7.3",
						FixedVersion:     "2.6.7, 3.1.1",
						Status:           dbTypes.StatusFixed,
					},
				},
			},
			want: `
package-lock.json (npm)
=======================
Total: 1 (MEDIUM: 0, HIGH: 1)

┌────────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬────────┐
│  Library   │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title  │
├────────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼────────┤
│ node-fetch │ CVE-2022-0235 │ HIGH     │ fixed  │ 1.7.3             │ 2.6.7, 3.1.1  │ foobar │
└────────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴────────┘

Dependency Origin Tree (Reversed)
=================================
package-lock.json
└── node-fetch@1.7.3, (MEDIUM: 0, HIGH: 1)
    └── ...(omitted)...
        └── styled-components@3.1.3
`,
		},
		{
			name: "show suppressed vulnerabilities",
			result: types.Result{
				Target: "test",
				Class:  types.ClassLangPkg,
				Vulnerabilities: []types.DetectedVulnerability{
					{
						VulnerabilityID:  "CVE-2020-0001",
						PkgName:          "foo",
						InstalledVersion: "1.2.3",
						PrimaryURL:       "https://avd.aquasec.com/nvd/cve-2020-0001",
						Status:           dbTypes.StatusWillNotFix,
						Vulnerability: dbTypes.Vulnerability{
							Title:       "title1",
							Description: "desc1",
							Severity:    "HIGH",
						},
					},
				},
				ModifiedFindings: []types.ModifiedFinding{
					{
						Type:      types.FindingTypeVulnerability,
						Status:    types.FindingStatusIgnored,
						Statement: "Not exploitable",
						Source:    ".trivyignore.yaml",
						Finding: types.DetectedVulnerability{
							VulnerabilityID:  "CVE-2020-0002",
							PkgName:          "bar",
							InstalledVersion: "1.2.3",
							Status:           dbTypes.StatusWillNotFix,
							Vulnerability: dbTypes.Vulnerability{
								Title:       "title2",
								Description: "desc2",
								Severity:    "MEDIUM",
							},
						},
					},
				},
			},
			showSuppressed: true,
			want: `
test ()
=======
Total: 1 (MEDIUM: 0, HIGH: 1)

┌─────────┬───────────────┬──────────┬──────────────┬───────────────────┬───────────────┬───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │    Status    │ Installed Version │ Fixed Version │                   Title                   │
├─────────┼───────────────┼──────────┼──────────────┼───────────────────┼───────────────┼───────────────────────────────────────────┤
│ foo     │ CVE-2020-0001 │ HIGH     │ will_not_fix │ 1.2.3             │               │ title1                                    │
│         │               │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2020-0001 │
└─────────┴───────────────┴──────────┴──────────────┴───────────────────┴───────────────┴───────────────────────────────────────────┘

Suppressed Vulnerabilities (Total: 1)
=====================================
┌─────────┬───────────────┬──────────┬─────────┬─────────────────┬───────────────────┐
│ Library │ Vulnerability │ Severity │ Status  │    Statement    │      Source       │
├─────────┼───────────────┼──────────┼─────────┼─────────────────┼───────────────────┤
│ bar     │ CVE-2020-0002 │ MEDIUM   │ ignored │ Not exploitable │ .trivyignore.yaml │
└─────────┴───────────────┴──────────┴─────────┴─────────────────┴───────────────────┘
`,
		},
		{
			name: "suppressed all OS package vulnerabilities without `showSuppressed` flag",
			result: types.Result{
				Target: "test",
				Class:  types.ClassOSPkg,
				Type:   ftypes.Alpine,
				ModifiedFindings: []types.ModifiedFinding{
					{
						Type:      types.FindingTypeVulnerability,
						Status:    types.FindingStatusIgnored,
						Statement: "Not exploitable",
						Source:    ".trivyignore.yaml",
						Finding: types.DetectedVulnerability{
							VulnerabilityID:  "CVE-2020-0001",
							PkgName:          "foo",
							InstalledVersion: "1.2.3",
							Status:           dbTypes.StatusWillNotFix,
							Vulnerability: dbTypes.Vulnerability{
								Title:       "title1",
								Description: "desc1",
								Severity:    "MEDIUM",
							},
						},
					},
				},
			},
			showSuppressed: false,
			want:           "",
		},
		{
			name: "suppressed all language package vulnerabilities without `showSuppressed` flag",
			result: types.Result{
				Target: "test",
				Class:  types.ClassLangPkg,
				Type:   ftypes.Jar,
				ModifiedFindings: []types.ModifiedFinding{
					{
						Type:      types.FindingTypeVulnerability,
						Status:    types.FindingStatusIgnored,
						Statement: "Not exploitable",
						Source:    ".trivyignore.yaml",
						Finding: types.DetectedVulnerability{
							VulnerabilityID:  "CVE-2020-0001",
							PkgName:          "foo",
							InstalledVersion: "1.2.3",
							Status:           dbTypes.StatusWillNotFix,
							Vulnerability: dbTypes.Vulnerability{
								Title:       "title1",
								Description: "desc1",
								Severity:    "MEDIUM",
							},
						},
					},
				},
			},
			showSuppressed: false,
			want:           ``,
		},
		{
			name: "suppressed all language package vulnerabilities with `showSuppressed` flag",
			result: types.Result{
				Target: "test",
				Class:  types.ClassLangPkg,
				Type:   ftypes.Jar,
				ModifiedFindings: []types.ModifiedFinding{
					{
						Type:      types.FindingTypeVulnerability,
						Status:    types.FindingStatusIgnored,
						Statement: "Not exploitable",
						Source:    ".trivyignore.yaml",
						Finding: types.DetectedVulnerability{
							VulnerabilityID:  "CVE-2020-0001",
							PkgName:          "foo",
							InstalledVersion: "1.2.3",
							Status:           dbTypes.StatusWillNotFix,
							Vulnerability: dbTypes.Vulnerability{
								Title:       "title1",
								Description: "desc1",
								Severity:    "MEDIUM",
							},
						},
					},
				},
			},
			showSuppressed: true,
			want: `
test (jar)
==========
Total: 0 (MEDIUM: 0, HIGH: 0)


Suppressed Vulnerabilities (Total: 1)
=====================================
┌─────────┬───────────────┬──────────┬─────────┬─────────────────┬───────────────────┐
│ Library │ Vulnerability │ Severity │ Status  │    Statement    │      Source       │
├─────────┼───────────────┼──────────┼─────────┼─────────────────┼───────────────────┤
│ foo     │ CVE-2020-0001 │ MEDIUM   │ ignored │ Not exploitable │ .trivyignore.yaml │
└─────────┴───────────────┴──────────┴─────────┴─────────────────┴───────────────────┘
`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			buf := bytes.NewBuffer([]byte{})
			r := table.NewVulnerabilityRenderer(buf, false, true, tt.showSuppressed, []dbTypes.Severity{
				dbTypes.SeverityHigh,
				dbTypes.SeverityMedium,
			})
			r.Render(tt.result)
			assert.Equal(t, tt.want, buf.String(), tt.name)
		})
	}
}
